TIP Swift Sample App/TwitterSearchViewController.swift (216 lines of code) (raw):

// // TwitterSearchViewController.swift // TwitterImagePipeline // // Created on 3/3/17. // Copyright © 2020 Twitter. All rights reserved. // import TwitterImagePipeline class TweetWithMediaTableViewCell: UITableViewCell, TIPImageViewFetchHelperDataSource, TIPImageViewFetchHelperDelegate { private var internalTweet: TweetInfo? var tweet: TweetInfo? { get { return self.internalTweet } set(newTweet) { self.internalTweet = newTweet self.tweetFetchHelper?.clearImage() self.tweetFetchHelper?.reload() } } private var tweetImageView: UIImageView? private var tweetFetchHelper: TIPImageViewFetchHelper? class func reuseIdentifier() -> String { return "TweetWithMedia" } init() { super.init(style: .subtitle, reuseIdentifier: TweetWithMediaTableViewCell.reuseIdentifier()) // logic is decoupled from the View via a "Helper" object // Create our helper self.tweetFetchHelper = TIPImageViewFetchHelper.init(delegate: self, dataSource: self) // Create our image view self.tweetImageView = UIImageView.init() self.tweetImageView!.tip_fetchHelper = self.tweetFetchHelper! self.tweetImageView!.contentMode = .scaleAspectFill self.tweetImageView!.clipsToBounds = true self.tweetImageView!.backgroundColor = UIColor.lightGray self.tweetImageView!.autoresizingMask = [.flexibleWidth, .flexibleHeight] // Add image view to content self.contentView.addSubview(self.tweetImageView!) } @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("\(#function) has not been implemented") } override func prepareForReuse() { super.prepareForReuse() self.tweet = nil } override func layoutSubviews() { super.layoutSubviews() var detailRect = self.detailTextLabel!.frame var textRect = self.textLabel!.frame let yDelta = detailRect.origin.y - textRect.origin.y textRect.origin.y = 3 detailRect.origin.y = textRect.origin.y + yDelta self.detailTextLabel!.frame = detailRect self.textLabel!.frame = textRect var imageRect = self.tweetImageView!.frame imageRect.origin.y = detailRect.origin.y + detailRect.size.height + 3 imageRect.origin.x = detailRect.origin.x imageRect.size.width = self.contentView.bounds.size.width - (2 * imageRect.origin.x) imageRect.size.height = self.contentView.bounds.size.height - (imageRect.origin.y + 3) self.tweetImageView!.frame = imageRect } // MARK: data source func tip_imagePipeline(for helper: TIPImageViewFetchHelper) -> TIPImagePipeline? { return APP_DELEGATE().imagePipeline } func tip_imageFetchRequest(for helper: TIPImageViewFetchHelper) -> TIPImageFetchRequest? { guard let tweetImage = self.tweet?.images.first else { return nil } let request = TweetImageFetchRequest.init(tweetImage: tweetImage, targetView: helper.fetchView) request.forcePlaceholder = APP_DELEGATE().usePlaceholder return request } // MARK: delegate func tip_fetchHelper(_ helper: TIPImageViewFetchHelper, shouldUpdateImageWithPreviewImageResult previewImageResult: TIPImageFetchResult) -> Bool { return true } func tip_fetchHelper(_ helper: TIPImageViewFetchHelper, shouldContinueLoadingAfterFetchingPreviewImageResult previewImageResult: TIPImageFetchResult) -> Bool { if previewImageResult.imageIsTreatedAsPlaceholder { return true } if let request = helper.fetchRequest, let options = request.options { if options.contains(.treatAsPlaceholder) { // would be a downgrade, stop return false } } guard let fetchImageView = helper.fetchView else { // don't have a view to compare with, stop return false } let originalDimensions = previewImageResult.imageOriginalDimensions let viewDimensions = TIPDimensionsFromView(fetchImageView) if (originalDimensions.height >= viewDimensions.height && originalDimensions.width >= viewDimensions.width) { return false } return true } func tip_fetchHelper(_ helper: TIPImageViewFetchHelper, shouldLoadProgressivelyWithIdentifier identifier: String, url URL: URL, imageType: String, originalDimensions: CGSize) -> Bool { return true } // @objc func tip_fetchHelper(_ helper: TIPImageViewFetchHelper, shouldReloadAfterDifferentFetchCompletedWith image: UIImage, dimensions: CGSize, identifier: String, url URL: URL, treatedAsPlaceholder placeholder: Bool, manuallyStored: Bool) -> Bool // { // // } } class TwitterSearchViewController: UIViewController, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource { private var searchController: UISearchController? private var tableView: UITableView? private var term: String? private var tweets: [TweetInfo]? override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) self.navigationItem.title = "Twitter Search" } @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("\(#function) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() self.searchController = UISearchController.init(searchResultsController: nil) self.searchController!.searchResultsUpdater = self self.searchController!.definesPresentationContext = true self.searchController!.searchBar.delegate = self self.searchController!.searchBar.sizeToFit() self.tableView = UITableView.init(frame: self.view.bounds, style: .plain) self.tableView!.autoresizingMask = [.flexibleWidth, .flexibleHeight] self.tableView!.delegate = self self.tableView!.dataSource = self self.tableView!.tableHeaderView = self.searchController!.searchBar self.view.addSubview(self.tableView!) } // MARK: table view func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let count = self.tweets?.count { return count } return 0 } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) if let imageInfo: TweetImageInfo = self.tweets?[indexPath.row].images.first { let vc = ZoomingTweetImageViewController.init(tweetImage: imageInfo) self.navigationController?.pushViewController(vc, animated: true) } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let hasImages: Bool let tweet = self.tweets?[indexPath.row] if let tweet = tweet { hasImages = tweet.images.count > 0 } else { hasImages = false } var cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: (hasImages) ? TweetWithMediaTableViewCell.reuseIdentifier() : "TweetNoMedia") if nil == cell { if hasImages { cell = TweetWithMediaTableViewCell.init() } else { cell = UITableViewCell.init(style: .subtitle, reuseIdentifier: "TweetNoMedia") } } cell!.textLabel?.text = tweet?.handle cell!.detailTextLabel?.text = tweet?.text if hasImages { (cell as! TweetWithMediaTableViewCell).tweet = tweet } return cell! } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if let tweet = self.tweets?[indexPath.row] { if tweet.images.count > 0 { return 180 } } return 44 } // @objc func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat // { // if let tweet = self.tweets?[indexPath.row] { // if tweet.images.count > 0 { // return 180 // } // } // // return 44 // } // MARK: Search Controller func updateSearchResults(for searchController: UISearchController) { } #if targetEnvironment(macCatalyst) func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { NSObject.cancelPreviousPerformRequests(withTarget: self) self.perform(#selector(_triggerSearch), with: nil, afterDelay: 0.5) } #endif func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { self._triggerSearch() } @objc func _triggerSearch() { guard let searchBar = self.searchController?.searchBar else { return } let search = searchBar.text self.term = search self.searchController!.isActive = false self.searchController!.searchBar.isUserInteractionEnabled = false searchBar.text = self.term TwitterAPI.sharedInstance().search(forTerm: (nil != self.term) ? self.term! : "", count: APP_DELEGATE().searchCount) { tweets, _ in self.tweets = tweets self.searchController?.searchBar.isUserInteractionEnabled = true self.tableView?.reloadData() } } func willPresentSearchController(_ searchController: UISearchController) { self.searchController!.searchBar.text = self.term } }